procedure ShowTime(nPixels, startTicks: integer);
var
  time: real;
  cr: string;
begin
  time := (TickCount - StartTicks) / 60;
  cr := chr(13);
  PutMessage(nPixels:1, ' pixels', cr, time:1:2, ' seconds',
     cr, nPixels/time:1:0, ' pixels/second');
end;

macro 'Fast Invert';
var
  width, height, StartTicks: integer;
begin
  GetPicSize(width,height);
  StartTicks := TickCount;
  Invert;
  ShowTime(width*height, StartTicks);
end;

macro 'Slow Invert';
{
This macro illustrates why it's not a good idea to use
macros for pixel-by-pixel processing.
}
var
  width,height,value,x,y,StartTicks: integer;
begin
  GetPicSize(width,height);
  if width = 0 then begin
     beep;
     PutMessage('Image required.');
     exit;
  end;
  StartTicks := TickCount;
  for y:=0 to height-1 do begin
    GetRow(0,y,width);
    for x:=0 to width-1 do LineBuffer[x]:=255-LineBuffer[x];
    PutRow(0,y,width);
  end;
  ShowTime(width*height, StartTicks);
end;

macro 'Real Slow Invert';
{
This macro illustrates why it's better to use GetRow
and PutRow instead of GetPixel and PutPixel.
}
var
  width,height,value,x,y,StartTicks: integer;
begin
  GetPicSize(width,height);
  if width = 0 then begin
     beep;
     PutMessage('Image required.');
     exit;
  end;
  StartTicks := TickCount;
  for y:=0 to height-1 do
    for x:=0 to width-1 do PutPixel(x, y, 255-GetPixel(x,y));
  ShowTime(width*height, StartTicks);
end;

macro '(---'; begin end;

macro 'Show Status [S]';
var
  roiType: integer;
begin
  NewTextWindow('Status');
  writeln('MaxMeasuements = ', Get('MaxMeasurements'):1);
  writeln('UndoBufSize = ', Get('UndoBufSize')/1024:1,'K');
  writeln('FreeMem = ', Get('FreeMem')/1024:1,'K');
  writeln('MaxBlock = ', Get('MaxBlock')/1024:1,'K');
  roiType := Get('RoiType');
  write('RoiType: ');
  if roiType = 0 then write('No ROI or no image')
  else if roiType = 1 then write('rectangle')
  else if roiType = 2 then write('ellipse')
  else if roiType = 3 then write('polygon')
  else if roiType = 4 then write('freehand')
  else if roiType = 5 then write('traced')
  else if roiType = 6 then write('straight line')
  else if roiType = 7 then write('freehand line')
  else if roiType = 8 then write('segmented line');
end

macro 'Draw Vertical Calibration Bar';
var
  left,top,width,height,i,x,y2,inc:integer;
  y:real;
begin
  GetRoi(left,top,width,height);
  if width=0 then begin
    beep;
    PutMessage('Make a rectangular selection first.');
    exit;
  end;
  SetFont('Helvetica');
  SetFontSize(10);
  SetText('Plain; Left; no background');
  SetLineWidth(1);
  Setforeground(255);
  DrawScale;
  x:=left;
  y:=top;
  inc:=height/10;
  for i:=1 to 11 do begin
    MoveTo(x+width+10,round(y)+2);
    y2:=round(y);
    if i=11 then y2:=y2-1;
    write(cvalue(GetPixel(x,y2)):1:2);
    y:=y+inc;
  end;
end;

macro 'ASCII Dump';
{
Generates an alphanumeric listing of pixels values starting at
the upper left corner of the current selection. 20 rows and 44 columns
can be displayed with the default 552 x 436 window.
}
var
  image,dump,roiLeft,roiTop,roiWidth,roiHeight:integer;
  h,v,value,MaxWidth,MaxHeight,width,height:integer;
begin
  image:=PicNumber;
  GetRoi(RoiLeft,RoiTop,RoiWidth,RoiHeight);
  if roiWidth=0 then begin
    beep;
    PutMessage('This macro requires a rectangular selection.');
    exit;
  end;
  SetForegroundColor(255);
  SetBackgroundColor(0);
  MakeNewWindow('ASCII Dump');
  dump:=PicNumber;
  GetPicSize(width,height);
  MaxWidth:=width div 24 - 2;
  MaxHeight:=height div 9 - 3;
  if roiWidth>MaxWidth then roiWidth:=MaxWidth;
  if roiHeight>MaxHeight then roiHeight:=MaxHeight;
  SetFont('Monaco');
  SetFontSize(9);
  SetText('With background; Left Justified');
  MoveTo(2,12);
  write('    ');
  for h:=roiLeft to roiLeft+roiWidth-1 do write(h:4);
  writeln;
  writeln;
  for v:=roiTop to roiTop+roiHeight-1 do begin
    write(v:3,' ');
    for h:=roiLeft to roiLeft+roiWidth-1 do begin
      ChoosePic(image);
      value:=GetPixel(h,v);
      ChoosePic(dump);
      write(value:4);
    end;
    writeln;
  end;
  ChoosePic(image);
end;

 
function hexDigit(digit: integer): string;
begin
		 if digit <= 9 then
					 hexDigit := chr(digit + ord('0'))
			else
					 hexDigit := chr(digit - 10 + ord('A'));
end;


function hex(value: integer): string;
begin
    hex := concat(hexDigit(value div 16), hexDigit(value mod 16));
end;


function GetByte(loc: integer): integer;
begin
    GetByte := GetPixel(loc mod width, loc div width);
end;


macro 'Hex Dump';
{
Generates a hex listing of pixels values starting at
the first byte of the image. It can be useful
for decoding image file headers.
}
var
  width, height, nLines, line: integer;
  i, j, BytesPerLine, loc, value: integer;
  image, ascii, char: string;
begin
  SaveState;
  nLines := 52;
  BytesPerLine := 10;
  image:=WindowTitle;
  GetPicSize(width, height);
  if width = 0 then begin
     beep;
     PutMessage('Image required.');
     exit;
  end;
  SetFont('Monaco');
  SetFontSize(9);
  NewTextWindow('Hex Dump');
  loc := 0;
  for line := 0 to nLines - 1 do begin
       write(loc:4, '  ');
       ascii := '  ';
       for i := 0 to BytesPerLine - 1 do begin
           value := GetByte(loc);
           write(hex(value), ' ');
           if (value >= 32) and (value <= 127) then
               char := chr(value)
           else
               char := '-';
           ascii := concat(ascii, char);
           loc := loc + 1;
       end;
       writeln(ascii);
  end;
  RestoreState;
end;


macro 'Scale and Rotate All';
{
Resizes and/or rotates all currently open widows. For example,
change the  ScaleAndRotate command below to
ScaleAndRotate(2,2,0)  to change the size of all the images
in a movie loop sequence from 128 x 128 to 256 x 256.
}
var
  i:integer;
begin
  SaveState;
  SetScaling('Bilinear; Create New Window');
  for i:=1 to nPics do begin
    ChoosePic(1);
    ScaleAndRotate(1.9,1.9,0);
    ChoosePic(1);
    Close;
  end;
  for i:=1 to nPics do begin
    ChoosePic(i);
    SetPicName(i);
  end;
  RestoreState;
end;


macro 'Dispose All';
begin
  DisposeAll;
end;

macro 'Average two Images';
  {Generates the arithmetic average of two images.}
begin
  RequiresVersion(1.53);
  if nPics<>2 then begin
    PutMessage('This macro requires exactly two image windows to be open.');
    Exit;
  End;
  ImageMath('add' ,1 ,2, 0.5, 0, 'Average');
 end;


macro 'Make Montage [M]';
{Opens a new window and creates in it a composite image made from all}
{currently open images. All the images must be the same size.}
var
  width,height,w,h,mWidth,mHeight,nWindows,left,top:integer;
  RoiWidth,RoiHeight,RoiWidth,RoiHeight,i,hloc,vloc:integer;
  montage,temp:integer;
  scale:real;
  SameSize:boolean;
begin
  nWindows:=nPics;
  SameSize:=true;
  GetPicSize(width,height);
  for i:=1 to nPics do begin
    SelectPic(i);
    GetPicSize(w,h);
    SameSize:=SameSize and (w=width) and (h=height);
  end;
  if (nWindows<2) or not SameSize then begin
    PutMessage('This macro needs two or more images of the same size in order to create a montage.');
    Exit;
  end;
  SetBackground(0);
  MakeNewWindow('Montage');
  montage:=nWindows+1;
  GetPicSize(mWidth,mHeight);
  SelectPic(1);
  Duplicate('Temp');
  temp:=nWindows+2;
  scale:=GetNumber('Scaling Factor:',0.25);
  hloc:=-(RoiWidth);
  vloc:=0;
  for i:=1 to nWindows do begin
    SelectPic(i);
    SelectAll;
    copy;
    SelectPic(temp);
    paste;
    SelectAll;
    ScaleSelection(scale,scale);
    RestoreRoi;
    if i=1 then begin
      GetRoi(left,top,RoiWidth,RoiHeight);
      hloc:=-RoiWidth;
      vloc:=0;
    end;
    Copy;
    SelectPic(montage);
    hloc:=hloc+RoiWidth;
    if (hloc+RoiWidth)>mWidth then begin
      hloc:=0;
      vloc:=vloc+RoiHeight;
    end;
    MakeRoi(hloc,vloc,RoiWidth,RoiHeight);
    Paste;
  end;
  KillRoi;
  SelectPic(temp);
  Dispose;
end;


macro 'Make Sine Wave';
var
  left,top,width,height,i:integer;
  ppp,scale:real;
begin
  SaveState;
  MakeNewWindow('Sine Wave');
  SelectAll;
  GetRoi(left,top,Width,Height);
  if width=0 then begin
    PutMessage('This macro requires a rectangular selection.');
    Exit;
  end;
  ppp:=GetNumber('Pixels per period',100);
  Scale:=ppp/6.28;
  MakeRoi(left,top,1,height);
  for i:=1 to width do begin
    SetForeground(sin(i/scale)*127 +128);
    {SetForeground((sin(i/scale)*127 +128)*(i+30)/(width));}
    {SetForeground(sin(i/(ppp*((width-i+3)/width)/6.28))*127 +128);}
    fill;
    MoveRoi(1,0);
  end;
  KillRoi;
  RestoreState;
end;

macro 'Beep if No Selection [B]';
var 
  left,top,width,height:integer;
begin
  GetRoi(left,top,width,height);
  if width=0 then beep;
end;

function power(x, n: real): real;
{raise x to the nth power}
begin
    power := exp(ln(x) * n);
end;


macro 'Exponention Demo';
var
    base, ex: real;
begin
    base := GetNumber('Base:', 2);
    ex := GetNumber('Exponent:', 5);
    PutMessage(power(base, ex):6:3);
end;

macro 'Convert Number to String Test';
var
    n: real;
    s1, s2, s3, s4: string;
begin
    n:=GetNumber('Enter a Number', 12.345);
    s1 := concat(n);
    s2 := concat(n:1:2);
    s3 := concat(n:10:4);
    s4 := concat(n:0);
    PutMessage('s1=',s1,', s2=',s2,', s3=',s3', s4=',s4);
end;


function factorial(n: integer):integer;
begin
   if n > 1 then
      factorial := n * factorial(n-1)
   else
      factorial := 1;
end;


macro 'Compute N Factorial...';
var
  n: integer;
begin
   n := GetNumber('N:', 3, 0);
   PutMessage(n:1, ' factoral = ', factorial(n):1);
end;


macro '(---'; begin end;

{These macros allow you to easily switch}
{transfer modes while pasting by tapping keys.}
macro 'Copy Mode[1]'; begin SetOption; DoCopy; end;
macro 'AND Mode[2]';  begin SetOption; DoAnd; end;
macro 'OR Mode [3]';  begin SetOption; DoOr; end;
macro 'XOR Mode[4]'; begin SetOption; DoXor; end;
macro 'REPLACE Mode[5]';  begin SetOption; DoReplace; end;
macro 'BLEND [6]';  begin SetOption; DoBlend; end;
macro 'Terminate Paste [7]'; begin KillRoi end;

